home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 3 / Info_Mac_1994-01.iso / Development / Source / JPEG Convert 1.0 Source / JPEG Convert1.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-02-11  |  45.7 KB  |  1,745 lines  |  [TEXT/KAHL]

  1. /*
  2.  * JPEG Convert1.c
  3.  *
  4.  * Copyright (C) 1992, James H. Brunner.
  5.  *
  6.  * This file is part of the "JPEG Convert" program.  The JPEG Convert program signature ('Ijgp')
  7.  * and unique file types ('TARG', 'RLE ', 'PPM ') are registered with Apple by the author.
  8.  *
  9.  * The JPEG Convert program is an image format conversion program that utilizes the software of
  10.  * the Independent JPEG Group.  The JPEG Convert program is essentially a Macintosh user interface
  11.  * around the Independent JPEG Group's code.  The author of JPEG Convert maintains a copyright to
  12.  * the interface software only.  For conditions of distribution and use of the Independent JPEG
  13.  * Group's software, refer to the README file contained in the Independent JPEG Group's distribution
  14.  * package.
  15.  *
  16.  * (Further use of the word 'software' refers only to the source files for implementing this user
  17.  *  interface, including the resource file which does not include this comment. It does NOT refer
  18.  *  to the Independent JPEG Group's code.)
  19.  *
  20.  * The conditions for distribution of this software are as follows:
  21.  *        This software may be freely distributed provided that it is not distributed for profit; a
  22.  *        nominal copying fee may be charged.
  23.  *
  24.  *        Any distribution of this software must contain all original copyright notices.
  25.  *
  26.  * The conditions for use of this software IN FULL are as follows:
  27.  *        This software may be used in full by any person or business provided that the person or
  28.  *        business is not seeking profits directly from the use of this software.
  29.  *
  30.  * The conditions for use of this software IN PART are as follows:
  31.  *        Any person or business may copy and utilize portions of this software in other products
  32.  *        provided that a note that "portions of the software are copyright by James H. Brunner"
  33.  *        is included in the software AND the resultant software is not intended to be distributed
  34.  *        for profit.
  35.  *
  36.  *        If SOURCE for projects containing portions of this code is not to be made available
  37.  *        with the resultant programs, an additional note that "portions of the software are 
  38.  *        copyright James H. Brunner" must be included in some visable user documentation.
  39.  *
  40.  * Bottom line:  I give it away free; I don't want you selling it.  You can use the source if
  41.  * you wish, but don't sell it.  If you use my work, give me credit for it.
  42.  */
  43.  
  44. /*
  45.  * JPEG Convert1.c
  46.  *
  47.  * This is part 1 of the Macintosh GUI (Graphical User Interface).  It is intended to be
  48.  * compiled on a Macintosh computer with Think C.  This contains almost all the "meat".  
  49.  * "JPEG Convert2.c" contains the source for the dialog boxes.  This contains the rest.
  50.  */
  51.  
  52.  
  53. #define MAC_COPYRIGHT    "Macintosh interface: Copyright (C) 1993, James H. Brunner"
  54.  
  55.  
  56. #include "JPEG Convert.h"
  57.  
  58. /* MAC INCLUDES */
  59. #include <Menus.h>
  60. #include <TextEdit.h>
  61. #include <Fonts.h>
  62. #include <Desk.h>
  63. #include <OSUtils.h>
  64. #include <Traps.h>
  65. #include <GestaltEqu.h>
  66. #include <Script.h>
  67. #include <BDC.h>
  68. #include <LoMem.h>
  69. #include <OSEvents.h>
  70.  
  71. /* UNIX INCLUDES */
  72. #include <errno.h>
  73. #include <setjmp.h>
  74.  
  75. /* INDEPENDENT JPEG GROUP INCLUDES */
  76. #include "jversion.h"
  77.  
  78. #define _DialogDispatch    0xAA68
  79.  
  80. #define    PROGRESS_DLOG    152
  81. #define REPLACE_DLOG    200
  82.  
  83. #define ERROR_ALERT_ID    666
  84.  
  85. #define MENUCOUNT    3
  86. #define APPLE_ID    128
  87. #define FILE_ID        129
  88. #define EDIT_ID        130
  89.  
  90. #define APPLE_M        0
  91. #define FILE_M        1
  92. #define EDIT_M        2
  93.  
  94. #define APPLE_ABOUT    1
  95.  
  96. #define FILE_OPEN    1
  97. #define FILE_ABORT    3
  98. #define FILE_PREFS    4
  99. #define FILE_QUIT    6
  100.  
  101. #define EDIT_UNDO    1
  102. #define EDIT_CUT    3
  103. #define EDIT_COPY    4
  104. #define EDIT_PASTE    5
  105. #define EDIT_CLEAR    6
  106.  
  107. #define PROG_ABORT    1
  108. #define    PROG_SCROLL    2
  109.  
  110. #define LONGJMP_ABORT    1
  111. #define LONGJMP_ERROR    2
  112.  
  113. #define VIS_MSG_LINES    9
  114. #define MEMORY_FUDGE    100000
  115. #define MAX_FILE_TYPES    20
  116.  
  117.  
  118. /* Use the preprocessor for a bit of error checking. */
  119.  
  120. #if DEFAULT_FMT == FMT_GIF
  121.     #ifndef GIF_SUPPORTED
  122.         #error The default format is unsupported!
  123.     #endif
  124. #elif DEFAULT_FMT == FMT_PPM
  125.     #ifndef PPM_SUPPORTED
  126.         #error The default format is unsupported!
  127.     #endif
  128. #elif DEFAULT_FMT == FMT_RLE
  129.     #ifndef RLE_SUPPORTED
  130.         #error The default format is unsupported!
  131.     #endif
  132. #elif DEFAULT_FMT == FMT_TARGA
  133.     #ifndef TARGA_SUPPORTED
  134.         #error The default format is unsupported!
  135.     #endif
  136. #elif DEFAULT_FMT == FMT_JPEG
  137.     #error The default format can not be JPEG
  138. #endif
  139.  
  140. #ifdef GIF_SUPPORTED
  141.     #ifndef QUANT_1PASS_SUPPORTED
  142.         #ifndef QUANT_2PASS_SUPPORTED
  143.             #error Must support quantizing for GIF
  144.         #endif
  145.     #endif
  146. #endif
  147.  
  148. /* GLOBAL VARS */
  149. static Boolean        gDone=FALSE;            /* TRUE when the program is quitting */
  150. static Boolean        gAbort=FALSE;            /* TRUE when the user has selected abort all */
  151. static Boolean        gInBackground=FALSE;    /* TRUE when application is in background */
  152. static Boolean        gProcessing=FALSE;        /* TRUE while JPEG code is processing */
  153. static Boolean        gDropStart=TRUE;        /* TRUE if application was launched by dropping file */
  154. static Boolean        gDirChanged=FALSE;        /* TRUE after we have changed the stdfile directory */
  155.  
  156. GLOBAL Boolean        gNewDialogMgr;            /* TRUE if the system 7 dialog manager is available */
  157. GLOBAL Boolean        gAppleEvents;            /* TRUE if system 7 apple events are available */
  158. GLOBAL Boolean        gColorQuickdraw;        /* TRUE if color qd and main screen > 4 colors */
  159. GLOBAL Boolean        gFSSpecSupport;            /* TRUE if machine supports FSSpecs */
  160. static Boolean        gWaitNextEvent;            /* TRUE if WaitNextEvent is available */
  161. static Boolean        gStandardFile58;        /* TRUE if the system 7 standard file calls are available */
  162.  
  163. static MenuHandle    gMenu[MENUCOUNT];        /* a list of the menu handles */
  164. static Rect            gDragRect;                /* the drag rect for the progress monitor */
  165.  
  166. static OSType        gTypeList[MAX_FILE_TYPES];    /* Type list for open file */
  167. static short        gTypeListCount;            /* number of items in gTypeList */
  168.  
  169. GLOBAL PrefHandle    gPrefs;                    /* User prefs from preference file and/or dialog */
  170.  
  171. static long            gMemory95;    /* Available memory in bytes less 5%.  This is measured once */
  172.                                 /*  at program startup because after the JPEG code uses      */
  173.                                 /*  malloc and free, who knows if those routines actually    */
  174.                                 /*  free the memory back to the Mac OS.                      */
  175.  
  176. static struct External_methods_struct    e_methods;        /* for the IJG code */
  177.  
  178. static ImageFormat        requested_fmt;        /* requested output format (DJPEG) */
  179. static jmp_buf            longjmp_data;        /* a long-jump buffer for error aborts */
  180. static boolean            is_targa;            /* records user -T switch */
  181.  
  182. static long                last_message_time;    /* time last message written to progress wind (ticks) */
  183. static TEHandle            errText;            /* handle to text in progress window */
  184. static short            curErrLine;            /* line number of top line of text in progress wind */
  185.  
  186. static DialogPtr        progressMonitorWindow;
  187. static Rect                progressTextView={95, 22, 194, 403};
  188. static Rect                progressBarRect={35, 20, 55, 420};
  189. static long                progressPercent400;        /* completion percent on a scale of 0-400 */
  190. static Str63            progressFileName;        /* file name displayed in progress window */
  191.  
  192.  
  193. static char        format_buffer[132];
  194. #define FORMAT1(text, data1)    (sprintf(format_buffer, (text), (data1)) ? (char *)&format_buffer : (char *)&format_buffer)
  195.  
  196.  
  197. LOCAL void
  198. ProcessOutstandingEvents (void);
  199.  
  200.  
  201. /* Stolen from TCL */
  202. LOCAL Boolean 
  203. TrapAvailable (short theTrap)
  204. {
  205.     TrapType tType;
  206.     short    numToolBoxTraps;
  207.  
  208.     tType = (theTrap & 0x800) > 0 ? ToolTrap : OSTrap;
  209.  
  210.     if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xAA6E, ToolTrap))
  211.         numToolBoxTraps = 0x200;
  212.     else
  213.         numToolBoxTraps = 0x400;
  214.  
  215.     if (tType == ToolTrap) {
  216.         theTrap &= 0x7FF;
  217.         if (theTrap >= numToolBoxTraps)
  218.             theTrap = _Unimplemented;
  219.     }
  220.  
  221.     return (NGetTrapAddress(theTrap, tType) != NGetTrapAddress(_Unimplemented, ToolTrap));
  222. }
  223.  
  224.  
  225. /* (from JDMAIN)
  226.  * This routine gets control after the input file header has been read.
  227.  * It must determine what output file format is to be written,
  228.  * and make any other decompression parameter changes that are desirable.
  229.  */
  230. METHODDEF void
  231. d_ui_method_selection (decompress_info_ptr cinfo)
  232. {
  233.   /* if grayscale or CMYK input, force similar output; */
  234.   /* else leave the output colorspace as set by options. */
  235.   if (cinfo->jpeg_color_space == CS_GRAYSCALE)
  236.     cinfo->out_color_space = CS_GRAYSCALE;
  237.   else if (cinfo->jpeg_color_space == CS_CMYK)
  238.     cinfo->out_color_space = CS_CMYK;
  239.  
  240.   /* select output file format */
  241.   /* Note: jselwxxx routine may make additional parameter changes,
  242.    * such as forcing color quantization if it's a colormapped format.
  243.    */
  244.   switch (requested_fmt) {
  245. #ifdef GIF_SUPPORTED
  246.   case FMT_GIF:
  247.     jselwgif(cinfo);
  248.     break;
  249. #endif
  250. #ifdef PPM_SUPPORTED
  251.   case FMT_PPM:
  252.     jselwppm(cinfo);
  253.     break;
  254. #endif
  255. #ifdef RLE_SUPPORTED
  256.   case FMT_RLE:
  257.     jselwrle(cinfo);
  258.     break;
  259. #endif
  260. #ifdef TARGA_SUPPORTED
  261.   case FMT_TARGA:
  262.     jselwtarga(cinfo);
  263.     break;
  264. #endif
  265.   default:
  266.     ERREXIT(cinfo->emethods, "Unsupported output file format");
  267.     break;
  268.   }
  269. }
  270.  
  271.  
  272. /* (from JCMAIN)
  273.  * select the input file type based on the first character of the input file and the TARGA flag
  274.  */
  275. LOCAL void
  276. select_file_type (compress_info_ptr cinfo)
  277. {
  278.   int c;
  279.  
  280.   if (is_targa) {
  281. #ifdef TARGA_SUPPORTED
  282.     jselrtarga(cinfo);
  283. #else
  284.     ERREXIT(cinfo->emethods, "Targa support was not compiled");
  285. #endif
  286.     return;
  287.   }
  288.  
  289.   if ((c = getc(cinfo->input_file)) == EOF)
  290.     ERREXIT(cinfo->emethods, "Empty input file");
  291.  
  292.   switch (c) {
  293. #ifdef GIF_SUPPORTED
  294.   case 'G':
  295.     jselrgif(cinfo);
  296.     break;
  297. #endif
  298. #ifdef PPM_SUPPORTED
  299.   case 'P':
  300.     jselrppm(cinfo);
  301.     break;
  302. #endif
  303. #ifdef RLE_SUPPORTED
  304.   case 'R':
  305.     jselrrle(cinfo);
  306.     break;
  307. #endif
  308. #ifdef TARGA_SUPPORTED
  309.   case 0x00:
  310.     jselrtarga(cinfo);
  311.     break;
  312. #endif
  313.   default:
  314. #ifdef TARGA_SUPPORTED
  315.     ERREXIT(cinfo->emethods, "Unrecognized input file format --- did you forget -T ?");
  316. #else
  317.     ERREXIT(cinfo->emethods, "Unrecognized input file format");
  318. #endif
  319.     break;
  320.   }
  321.  
  322.   if (ungetc(c, cinfo->input_file) == EOF)
  323.     ERREXIT(cinfo->emethods, "ungetc failed");
  324. }
  325.  
  326.  
  327. /* (from JCMAIN)
  328.  * This routine gets control after the input file header has been read.
  329.  * It must determine what output JPEG file format is to be written,
  330.  * and make any other compression parameter changes that are desirable.
  331.  */
  332.  
  333. METHODDEF void
  334. c_ui_method_selection (compress_info_ptr cinfo)
  335. {
  336.   /* If the input is gray scale, generate a monochrome JPEG file. */
  337.   if (cinfo->in_color_space == CS_GRAYSCALE)
  338.     j_monochrome_default(cinfo);
  339.   /* For now, always select JFIF output format. */
  340. #ifdef JFIF_SUPPORTED
  341.   jselwjfif(cinfo);
  342. #else
  343.     #error You must have JFIF_SUPPORTED
  344. #endif
  345. }
  346.  
  347.  
  348. /*
  349.  *    Display an error dialog
  350.  */
  351. GLOBAL void
  352. Error (OSErr eCode, unsigned char *p1, unsigned char *p2)
  353. {
  354.     Str63    p0;
  355.     long    p0l;
  356.  
  357.     if (gAppleEvents)
  358.         if (AEInteractWithUser(kAEDefaultTimeout, NULL, NULL)) {
  359.             SysBeep(0L);
  360.             return;
  361.         }
  362.     
  363.     p0l = eCode;
  364.     NumToString(p0l, p0);
  365.     ParamText(p0, p1, p2, NULL);
  366.     Alert(ERROR_ALERT_ID, NULL);
  367. }
  368.  
  369.  
  370. /* This is the Mac trace_message routine.  It adds a line of text to the textEdit item and 
  371.  * updates the dialog (which better be active).  This routine also processes all of the events
  372.  * that may have occurred since this interface was last in control.
  373.  */
  374. METHODDEF void
  375. trace_message (const char *msgtext)
  376. {
  377.     char            msgbuffer[256];
  378.     long            msglen;
  379.     ControlHandle    scrollHand;
  380.     int                original_lines;
  381.     int                additional_lines;
  382.  
  383.     ProcessOutstandingEvents();
  384.  
  385.     msglen = sprintf(msgbuffer, msgtext,
  386.         e_methods.message_parm[0], e_methods.message_parm[1],
  387.         e_methods.message_parm[2], e_methods.message_parm[3],
  388.         e_methods.message_parm[4], e_methods.message_parm[5],
  389.         e_methods.message_parm[6], e_methods.message_parm[7]);
  390.     
  391.     msgbuffer[msglen++] = '\r';
  392.  
  393.     original_lines = (**errText).nLines;
  394.     TEInsert(msgbuffer, msglen, errText);
  395.     additional_lines = (**errText).nLines - original_lines;
  396.  
  397.     scrollHand = CITEMH(progressMonitorWindow, PROG_SCROLL);
  398.     if ((**errText).nLines > VIS_MSG_LINES) {
  399.         if (GetCtlValue(scrollHand) == GetCtlMax(scrollHand)) {
  400.             SetCtlMax(scrollHand, (**errText).nLines - VIS_MSG_LINES);
  401.             SetCtlValue(scrollHand, (**errText).nLines - VIS_MSG_LINES);
  402.             TEScroll(0, -additional_lines * (**errText).lineHeight, errText);
  403.             curErrLine += additional_lines;
  404.         } else {
  405.             SetCtlMax(scrollHand, (**errText).nLines - VIS_MSG_LINES);
  406.         }
  407.     } else {
  408.         TEUpdate(&progressTextView, errText);
  409.     }
  410.     
  411.     last_message_time = TickCount();
  412.     
  413.     ProcessOutstandingEvents();
  414. }
  415.  
  416.  
  417. /* This is the Mac error_exit routine.  It generates an error dialog, frees all allocated memory,
  418.  * and longjmp's back to the dialog routine (we don't want to "exit(0)").
  419.  */
  420. METHODDEF void
  421. error_exit (const char *msgtext)
  422. {
  423.     long    ticks;
  424.     char    msgbuffer[256];
  425.  
  426.     e_methods.free_all();    /* clean up memory allocation */
  427.  
  428.     sprintf(msgbuffer, msgtext,
  429.         e_methods.message_parm[0], e_methods.message_parm[1],
  430.         e_methods.message_parm[2], e_methods.message_parm[3],
  431.         e_methods.message_parm[4], e_methods.message_parm[5],
  432.         e_methods.message_parm[6], e_methods.message_parm[7]);
  433.     CtoPstr(msgbuffer);
  434.     Error(0, "\pUnrecoverable error:", (PSTR)msgbuffer);
  435.     
  436.     longjmp(longjmp_data, LONGJMP_ERROR);
  437. }
  438.  
  439.  
  440. /* This routine sets up our error and trace message routines
  441.  */
  442. LOCAL void
  443. macselerror ()
  444. {
  445.     e_methods.error_exit = error_exit;
  446.     e_methods.trace_message = trace_message;
  447.  
  448.     e_methods.trace_level = 0;    /* default = no tracing */
  449.  
  450.     e_methods.num_warnings = 0;    /* no warnings emitted yet */
  451.     /* By default, the first corrupt-data warning will be displayed,
  452.      * but additional ones will appear only if trace level is at least 2.
  453.      * A corrupt data file could generate many warnings, so it's a good idea
  454.      * to suppress additional messages at default tracing level.
  455.      */
  456.     e_methods.first_warning_level = 0;
  457.     e_methods.more_warning_level = 2;
  458. }
  459.  
  460.  
  461. /*
  462.  * Optional routine to display a percent-done figure on stderr.
  463.  * See jcdeflts.c for explanation of the information used.
  464.  */
  465. LOCAL void
  466. draw_progress_monitor (completeUpdate)
  467.     Boolean    completeUpdate;
  468. {
  469.     Rect    r;
  470.     
  471.     if (completeUpdate) {
  472.         TextFont(0);    // For some reason, if I don't do this, under system 6,
  473.         TextFace(0);    //  the static text in the dialog draws fine the first time
  474.         TextMode(1);    //  and then, after the TE item is drawn, the window's font
  475.         TextSize(0);    //  changes to match that of the text item.  Go figure.
  476.  
  477.         ParamText(progressFileName, NULL, NULL, NULL);
  478.         DrawDialog(progressMonitorWindow);
  479.  
  480.         r = progressBarRect;
  481.         InsetRect(&r, -2, -2);
  482.         PenSize(2, 2);
  483.         FrameRect(&r);
  484.  
  485.         r = progressTextView;
  486.         InsetRect(&r, -4, -2);
  487.         PenSize(1, 1);
  488.         FrameRect(&r);
  489.  
  490.         TEUpdate(&progressTextView, errText);
  491.     }
  492.     
  493.     r = progressBarRect;
  494.     r.right = 20 + progressPercent400;
  495.  
  496.     ForeColor(blueColor);
  497.     FillRect(&r, gray);
  498.     ForeColor(blackColor);
  499. }
  500.  
  501.  
  502. /* Action proc for the scroll bar in the progress monitor.  This routine takes case of
  503.  * scrolling the text up or down (lines or pages) while the mouse is held down in the
  504.  * scroll bar.
  505.  */
  506. METHODDEF pascal void
  507. scroll_action_proc (ControlHandle theControl, short partCode)
  508. {
  509.     short    currentValue;
  510.     short    boundingValue;
  511.     short    delta;
  512.  
  513.     currentValue = GetCtlValue(theControl);
  514.     switch (partCode) {
  515.         case inUpButton:
  516.             boundingValue = GetCtlMin(theControl);
  517.             if (currentValue > boundingValue) {
  518.                 SetCtlValue(theControl, currentValue-1);
  519.                 TEScroll(0, (**errText).lineHeight, errText);
  520.                 curErrLine--;
  521.             }
  522.             break;
  523.         case inDownButton:
  524.             boundingValue = GetCtlMax(theControl);
  525.             if (currentValue < boundingValue) {
  526.                 SetCtlValue(theControl, currentValue+1);
  527.                 TEScroll(0, -(**errText).lineHeight, errText);
  528.                 curErrLine++;
  529.             }
  530.             break;
  531.         case inPageUp:
  532.             boundingValue = GetCtlMin(theControl);
  533.             if (currentValue > boundingValue) {
  534.                 delta = MIN(currentValue-boundingValue, VIS_MSG_LINES-1);
  535.                 SetCtlValue(theControl, currentValue-delta);
  536.                 TEScroll(0, delta * (**errText).lineHeight, errText);
  537.                 curErrLine -= delta;
  538.             }
  539.             break;
  540.         case inPageDown:
  541.             boundingValue = GetCtlMax(theControl);
  542.             if (currentValue < boundingValue) {
  543.                 delta = MIN(boundingValue-currentValue, VIS_MSG_LINES-1);
  544.                 SetCtlValue(theControl, currentValue+delta);
  545.                 TEScroll(0, -delta * (**errText).lineHeight, errText);
  546.                 curErrLine += delta;
  547.             }
  548.             break;
  549.     }
  550. }
  551.  
  552.  
  553. /* This routine displays the progress monitor dialog window.  It allocates the textEdit record
  554.  * used for trace messages.  It initializes the bar graph to zero and sets the gProcessing flag.
  555.  *
  556.  * This interface assumes that if gProcessing is true, that it is OK to longjmp on an error.  Make
  557.  * sure that setjmp is called right after this routine (before any events are handled).
  558.  */
  559. LOCAL void
  560. open_progress_monitor (fileName)
  561.     Str63    fileName;
  562. {
  563.     int        i;
  564.     static Boolean    checked_window_loc=FALSE;
  565.     
  566.     for (i=0;  i <= fileName[0];  i++)
  567.         progressFileName[i] = fileName[i];
  568.  
  569.     progressMonitorWindow = GetNewDialog(PROGRESS_DLOG, NULL, (WindowPtr)-1);
  570.     
  571.     MoveWindow(progressMonitorWindow, (**gPrefs).progressLoc.h, (**gPrefs).progressLoc.v, FALSE);
  572.     ShowWindow(progressMonitorWindow);
  573.  
  574.     /* make sure the user can grab the title bar.  He may have changed monitor config, etc */
  575.     if (!checked_window_loc) {
  576.         Rect    titleBarRect;
  577.         
  578.         checked_window_loc = TRUE;
  579.         
  580.         titleBarRect = (**((*(WindowPeek)progressMonitorWindow).strucRgn)).rgnBBox;
  581.         titleBarRect.bottom = (**((*(WindowPeek)progressMonitorWindow).contRgn)).rgnBBox.top;
  582.         
  583.         if (!SectRect(&titleBarRect, &gDragRect, &titleBarRect)) {
  584.             (**gPrefs).progressLoc.h = 20;
  585.             (**gPrefs).progressLoc.v = 40;
  586.             ChangedResource((Handle)gPrefs);
  587.             MoveWindow(progressMonitorWindow, (**gPrefs).progressLoc.h, (**gPrefs).progressLoc.v, FALSE);
  588.         }
  589.     }
  590.  
  591.     SetPort(progressMonitorWindow);
  592.     errText = TENew(&progressTextView, &progressTextView);
  593.     (**errText).txFont = monaco;
  594.     (**errText).txFace = 0;
  595.     (**errText).txSize = 9;
  596.     (**errText).lineHeight = 11;
  597.     (**errText).fontAscent = 9;
  598.     
  599.     curErrLine = 0;
  600.     
  601.     gProcessing = TRUE;
  602.     progressPercent400 = 0;
  603.     
  604.     draw_progress_monitor(TRUE);
  605. }
  606.  
  607.  
  608. /* This routine removed the progress monitor from the screen and deallocates the testEdit
  609.  * record used for trace messages.  This routine also checks the time when the last message
  610.  * was written to the window and will pause to make sure that the user has had time to
  611.  * read the message before removing the window.
  612.  */
  613. LOCAL void
  614. close_progress_monitor ()
  615. {
  616.     long    ticks_since_last_msg;
  617.     
  618.     ticks_since_last_msg = TickCount() - last_message_time;
  619.     if (ticks_since_last_msg < 60)
  620.         Delay(60 - ticks_since_last_msg, &ticks_since_last_msg);
  621.  
  622.     DisposDialog(progressMonitorWindow);
  623.     
  624.     TEDispose(errText);
  625.     
  626.     gProcessing = FALSE;
  627. }
  628.  
  629.  
  630. /* Process events and update the bar graph according to progress.
  631.  */
  632. LOCAL void
  633. progress_monitor (completed_passes, total_passes, loopcounter, looplimit)
  634.     int        completed_passes, total_passes;
  635.     long    loopcounter, looplimit;
  636. {
  637.     ProcessOutstandingEvents();
  638.  
  639.     progressPercent400 = 400 * completed_passes / total_passes;
  640.     progressPercent400 += ((400 * loopcounter) / total_passes) / looplimit;
  641.  
  642.     draw_progress_monitor(FALSE);
  643. }
  644.  
  645.  
  646. /* These two routines are the progress monitor interfaces for DJPEG and CJPEG.
  647.  * They simply call the common progress_monitor routine.
  648.  */
  649. METHODDEF void
  650. c_progress_monitor (compress_info_ptr cinfo, long loopcounter, long looplimit)
  651. {
  652.     progress_monitor(cinfo->completed_passes, cinfo->total_passes, loopcounter, looplimit);
  653. }
  654. METHODDEF void
  655. d_progress_monitor (decompress_info_ptr cinfo, long loopcounter, long looplimit)
  656. {
  657.     progress_monitor(cinfo->completed_passes, cinfo->total_passes, loopcounter, looplimit);
  658. }
  659.  
  660.  
  661. /* Given a file spec, return a path name */
  662. GLOBAL OSErr
  663. FSSpecToName (FSSpec spec, Str255 fullname)
  664. {
  665.     Str63            dirName;    /* allow room for a 31 char file/dir name + a ':' */
  666.     CInfoPBRec        di;
  667.     OSErr            err;
  668.     
  669.     fullname[0] = 0;
  670.  
  671.     dirName[0] = 0;
  672.     di.dirInfo.ioNamePtr = dirName;
  673.     di.dirInfo.ioDrParID = spec.parID;
  674.     
  675.     BlockMove(spec.name, fullname, spec.name[0]+1);
  676.  
  677.     do {
  678.         di.dirInfo.ioVRefNum = spec.vRefNum;
  679.         di.dirInfo.ioFDirIndex = -1;
  680.         di.dirInfo.ioDrDirID = di.dirInfo.ioDrParID;
  681.         
  682.         err = PBGetCatInfo(&di, FALSE);
  683.         if (err == noErr) {
  684.             dirName[++dirName[0]] = ':';
  685.             if ((int)dirName[0] + (int)fullname[0] > 255)
  686.                 return bdNamErr;
  687.             else
  688.                 CONCAT(fullname, dirName, fullname);
  689.         } else
  690.             return err;
  691.     } while (di.dirInfo.ioDrDirID != fsRtDirID);
  692.  
  693.     return noErr;
  694. }
  695.  
  696.  
  697. /* CenterRect:  Center a rectangle on the main screen */
  698. LOCAL void
  699. CenterRect (Rect *r)
  700. {
  701.     short    delta;
  702.     short    screenTop;
  703.  
  704.     delta = r->right - r->left;
  705.     r->left  = (screenBits.bounds.right - delta) >> 1;
  706.     r->right = r->left + delta;
  707.  
  708.     /* Determine top of screen */
  709.     screenTop = GetMBarHeight();
  710.  
  711.     /* Place it in upper third of screen */
  712.     delta = r->bottom - r->top;
  713.     r->top = (screenBits.bounds.bottom - screenTop - delta) / 3 + screenTop;
  714.     r->bottom = r->top + delta;
  715. }
  716.  
  717.  
  718. /* CenterDialog: Center a dialog on the main screen before creating it */
  719. GLOBAL void
  720. CenterDialog (short id, Point *corner)
  721. {
  722.     DialogTHndl    theDLOG;
  723.     Rect        bounds;
  724.  
  725.     /* Get the DLOG resource */
  726.     theDLOG = (DialogTHndl)GetResource('DLOG', id);
  727.     if (!theDLOG)
  728.         return;
  729.  
  730.     /* Center it within screenBits */
  731.     bounds = (*theDLOG)->boundsRect;
  732.     CenterRect(&bounds);
  733.     (*theDLOG)->boundsRect = bounds;
  734.     
  735.     if (corner)
  736.         *corner = topLeft(bounds);
  737. }
  738.  
  739.  
  740. /*
  741.  * This routine will set the file type and creator for the given file to whatever is appropriate
  742.  * for the given ImageFormat.
  743.  */
  744. LOCAL void
  745. set_file_type (FSSpec file, ImageFormat format)
  746. {
  747.     FInfo    finfo;
  748.     OSErr    err;
  749.     Str255    tempName;
  750.     
  751.     FSSpecToName(file, tempName);
  752.     err = GetFInfo(tempName, file.vRefNum, &finfo);
  753.     if (err == noErr) {
  754.         switch (format) {
  755.             case FMT_GIF:
  756.                 finfo.fdType = (**gPrefs).gif_type;
  757.                 finfo.fdCreator = (**gPrefs).gif_creator;
  758.                 break;
  759.             case FMT_PPM:
  760.                 finfo.fdType = (**gPrefs).ppm_type;
  761.                 finfo.fdCreator = (**gPrefs).ppm_creator;
  762.                 break;
  763.             case FMT_RLE:
  764.                 finfo.fdType = (**gPrefs).rle_type;
  765.                 finfo.fdCreator = (**gPrefs).rle_creator;
  766.                 break;
  767.             case FMT_TARGA:
  768.                 finfo.fdType = (**gPrefs).targa_type;
  769.                 finfo.fdCreator = (**gPrefs).targa_creator;
  770.                 break;
  771.             case FMT_JPEG:
  772.                 finfo.fdType = (**gPrefs).jpeg_type;
  773.                 finfo.fdCreator = (**gPrefs).jpeg_creator;
  774.                 break;
  775.             default:
  776.                 finfo.fdType = '????';            /* wasn't expecting this... */
  777.                 finfo.fdCreator = appSignature;
  778.         }
  779.         SetFInfo(tempName, file.vRefNum, &finfo);
  780.     }
  781. }
  782.  
  783.  
  784. /*
  785.  * This routine checks for the existance of a file.  If it exists, the user is prompted to
  786.  * see if it is ok to replace the file.  If the user says OK or if the file does not exist,
  787.  * true is returned.
  788.  */
  789. LOCAL Boolean
  790. ok_to_replace (FSSpec file)
  791. {
  792.     DialogPtr    d;
  793.     Boolean        replace_ok=FALSE;
  794.     short        item;
  795.     OSErr        err;
  796.     FSSpec        tempSpec;
  797.  
  798.     /* If user pref is to overwrite, just return TRUE */
  799.     if ((**gPrefs).overwrite)
  800.         return TRUE;
  801.     
  802.     /* does the file exist? */
  803.     err = xFSMakeFSSpec(file.vRefNum, file.parID, file.name, &tempSpec);
  804.     replace_ok = (err == fnfErr);
  805.     
  806.     /* if it exists, ask if OK to replace. */
  807.     if (!replace_ok) {
  808.         if (gAppleEvents)
  809.             if (AEInteractWithUser(kAEDefaultTimeout, NULL, NULL)) {
  810.                 SysBeep(0L);
  811.                 return FALSE;
  812.             }
  813.  
  814.         if (!gNewDialogMgr)
  815.             CenterDialog(REPLACE_DLOG, NULL);
  816.     
  817.         d = GetNewDialog(REPLACE_DLOG, NULL, (WindowPtr)-1);
  818.         ParamText(file.name, NULL, NULL, NULL);
  819.  
  820.         /* If using the new dialog manager, border the cancel button and set as a default */
  821.         /* If using system 6, will just use plain old buttons.  Easier. */
  822.         if (gNewDialogMgr) {
  823.             SetDialogDefaultItem(d, cancel);
  824.             SetDialogCancelItem(d, cancel);
  825.         }
  826.  
  827.         ShowWindow(d);
  828.         ModalDialog(NULL, &item);
  829.         replace_ok = (item == ok);
  830.         DisposDialog(d);
  831.     }
  832.     
  833.     return replace_ok;
  834. }
  835.  
  836.  
  837. /*
  838.  *    This is the routine that does all of the work when a jpeg file is dropped.  It calls the
  839.  *  routine to diaplay the dialog (if necessary), and calls the jpeg routine to do the 
  840.  *  processing work.
  841.  */
  842. LOCAL void
  843. process_a_jpeg_file (FSSpec inputFile, char *fullinfile)
  844. {
  845.     static Boolean    do_all=FALSE;
  846.     static djpeg_dlog_data dlog_info;
  847.     Str255            fulloutfile;
  848.     short            item;
  849.     int                longjmp_reason;
  850.     long            curTicks;
  851.  
  852.     /*    Have we been here before and did the user hit the OK ALL button?  If not, we dialog. */    
  853.     if (!do_all) {
  854.         switch (item = display_djpeg_dialog(inputFile, &dlog_info)) {
  855.             case J_OK_ALL:
  856.                 do_all = TRUE;
  857.             case J_OK:
  858.                 break;
  859.         }
  860.     } else {
  861.         /* They hit OK_ALL on a previous file.  Just create a unique name. */
  862.         fix_name(dlog_info.outfile.name, inputFile.name, requested_fmt);
  863.         dlog_info.replace_ok = FALSE;
  864.     }
  865.  
  866.     FSSpecToName(dlog_info.outfile, fulloutfile);
  867.     PtoCstr(fulloutfile);
  868.     
  869.     /* We're gonna do it.  Set up the structures, open the files, and do it to it... */
  870.     if (do_all  ||  item == J_OK) {
  871.         struct Decompress_info_struct        cinfo;
  872.         struct Decompress_methods_struct    dc_methods;
  873.  
  874.         /* Check if it's ok write the output file */
  875.         if (!dlog_info.replace_ok)
  876.             if (!ok_to_replace(dlog_info.outfile))
  877.                 return;
  878.  
  879.         /* Initialize the system-dependent method pointers. */
  880.         cinfo.methods = &dc_methods;
  881.         cinfo.emethods = &e_methods;
  882.         macselerror();    /* error/trace message routines */
  883.         jselmemmgr(&e_methods);    /* memory allocation routines */
  884.         dc_methods.d_ui_method_selection = d_ui_method_selection;
  885.  
  886.         /* Set up default JPEG parameters. */
  887.         j_d_defaults(&cinfo, TRUE);
  888.  
  889.         /* Scan command line options, adjust parameters */
  890.         if (dlog_info.smoothing)
  891.             cinfo.do_block_smoothing = TRUE;
  892.  
  893.         if (dlog_info.grayscale)
  894.             cinfo.out_color_space = CS_GRAYSCALE;
  895.         
  896.         if (dlog_info.quantize) {
  897.             cinfo.quantize_colors = TRUE;
  898.             cinfo.desired_number_of_colors = dlog_info.colors;
  899.         }
  900.         
  901.         if (dlog_info.onepass)
  902.             cinfo.two_pass_quantize = FALSE;
  903.  
  904.         if (dlog_info.nodither)
  905.             cinfo.use_dithering = FALSE;
  906.             
  907.         e_methods.trace_level = dlog_info.debug;
  908.         e_methods.max_memory_to_use = gMemory95;
  909.  
  910.         /* Start up progress display */
  911.         dc_methods.progress_monitor = d_progress_monitor;
  912.  
  913.         /* Do it to it! */
  914.         open_progress_monitor(inputFile.name);
  915.         if (longjmp_reason = setjmp(longjmp_data)) {            /* An error or an abort */
  916.             switch (longjmp_reason) {
  917.                 case LONGJMP_ABORT:
  918.                     WARNMS(&e_methods, "processing aborted.");
  919.                     e_methods.free_all();    /* clean up memory allocation */
  920.                     break;
  921.                 case LONGJMP_ERROR:
  922.                     break;
  923.             }
  924.             fclose(stdin);
  925.             fclose(stdout);
  926.             CtoPstr((CSTR)fulloutfile);
  927.             FSDelete(fulloutfile, dlog_info.outfile.vRefNum);
  928.             close_progress_monitor();
  929.             return;
  930.         }
  931.  
  932.         /* print version identification */
  933.         TRACEMS(&e_methods, 0, FORMAT1("Independent JPEG Group's DJPEG, version %s", JVERSION));
  934.         TRACEMS(&e_methods, 1, JCOPYRIGHT);
  935.         TRACEMS(&e_methods, 1, MAC_COPYRIGHT);
  936.  
  937.         /* Select the input and output files */
  938.         PtoCstr(inputFile.name);
  939.         TRACEMS(&e_methods, 1, FORMAT1("converting %s from jpeg format.", inputFile.name));
  940.         CtoPstr((CSTR)inputFile.name);
  941.  
  942.         TRACEMS(&e_methods, 2, FORMAT1("input file path: %s", fullinfile));
  943.         freopen((CSTR)fullinfile, "rb", stdin);
  944.         if ((cinfo.input_file = stdin) == NULL)
  945.             ERREXIT(&e_methods, FORMAT1("can't open %s", fullinfile));
  946.  
  947.         TRACEMS(&e_methods, 2, FORMAT1("output file path: %s", fulloutfile));
  948.         freopen((CSTR)fulloutfile, "wb", stdout);
  949.         if ((cinfo.output_file = stdout) == NULL)
  950.             ERREXIT(&e_methods, FORMAT1("can't open %s", fulloutfile));
  951.  
  952.         /* Set up to read a JFIF or baseline-JPEG file. */
  953.         /* A smarter UI would inspect the first few bytes of the input file */
  954.         /* to determine its type. */
  955. #ifdef JFIF_SUPPORTED
  956.         jselrjfif(&cinfo);
  957. #else
  958.     #error You must have JFIF_SUPPORTED
  959. #endif
  960.  
  961.         requested_fmt = dlog_info.format;
  962.         set_file_type(dlog_info.outfile, requested_fmt);
  963.         jpeg_decompress(&cinfo);
  964.         progress_monitor(1,1,0,1);
  965.         Delay(30, &curTicks);
  966.  
  967.         close_progress_monitor();
  968.     }
  969. }
  970.  
  971.  
  972. /*
  973.  *    This is the routine that does all of the work when a non-jpeg file is dropped.  It calls 
  974.  *  a routine to display the dialog (if necessary), and calls the jpeg routine to do the
  975.  *  processing work.
  976.  */
  977. LOCAL void
  978. process_a_nonjpeg_file (FSSpec inputFile, char *fullinfile)
  979. {
  980.     static Boolean    do_all=FALSE;
  981.     static cjpeg_dlog_data    dlog_info;
  982.     Str255            fulloutfile;
  983.     short            item;
  984.     int                longjmp_reason;
  985.     long            curTicks;
  986.  
  987.     /*    Have we been here before and did the user hit the OK ALL button?  If not, we dialog. */    
  988.     if (!do_all) {
  989.         switch (item = display_cjpeg_dialog(inputFile, &dlog_info)) {
  990.             case J_OK_ALL:
  991.                 do_all = TRUE;
  992.             case J_OK:
  993.                 break;
  994.         }
  995.     } else {
  996.         /* If do all was selected on a previous dialog, just generate the output file name */
  997.         fix_name(dlog_info.outfile.name, inputFile.name, FMT_JPEG);
  998.         dlog_info.replace_ok = FALSE;
  999.     }
  1000.  
  1001.     FSSpecToName(dlog_info.outfile, fulloutfile);
  1002.     PtoCstr(fulloutfile);
  1003.  
  1004.     /* And now, set up the data structures, open the files, and do it to it. */
  1005.     if (do_all  ||  item == J_OK) {
  1006.         struct Compress_info_struct        cinfo;
  1007.         struct Compress_methods_struct    c_methods;
  1008.  
  1009.         /* Check if it's ok write the output file */
  1010.         if (!dlog_info.replace_ok)
  1011.             if (!ok_to_replace(dlog_info.outfile))
  1012.                 return;
  1013.  
  1014.         /* Initialize the system-dependent method pointers. */
  1015.         cinfo.methods = &c_methods;
  1016.         cinfo.emethods = &e_methods;
  1017.         macselerror();    /* error/trace message routines */
  1018.         jselmemmgr(&e_methods);    /* memory allocation routines */
  1019.         c_methods.c_ui_method_selection = c_ui_method_selection;
  1020.         
  1021.         /* Set up default JPEG parameters. */
  1022.         j_c_defaults(&cinfo, 75, FALSE); /* default quality level = 75 */
  1023.         
  1024.         /* Scan command line options, adjust parameters */
  1025.         if (dlog_info.grayscale)
  1026.             j_monochrome_default(&cinfo);
  1027.  
  1028.         /* Note: for now, we make force_baseline FALSE.
  1029.           * This means non-baseline JPEG files can be created with low Q values.
  1030.           * To ensure only baseline files are generated, pass TRUE instead.
  1031.           */
  1032.         j_set_quality(&cinfo, dlog_info.quality, FALSE);
  1033.  
  1034.         is_targa = dlog_info.targa;
  1035.  
  1036.         if (dlog_info.optimize)
  1037.             cinfo.optimize_coding = TRUE;
  1038.  
  1039. #ifdef ARITH_CODING_SUPPORTED
  1040.         if (dlog_info.arithmetic)
  1041.             cinfo.arith_code = TRUE;
  1042. #endif
  1043. #ifdef MULTISCAN_FILES_SUPPORTED
  1044.         if (dlog_info.nointerleave)
  1045.             cinfo.interleave = FALSE;
  1046. #endif
  1047.  
  1048.         e_methods.trace_level = dlog_info.debug;        
  1049.         e_methods.max_memory_to_use = gMemory95;
  1050.  
  1051.         /* Start up progress display */
  1052.         c_methods.progress_monitor = c_progress_monitor;
  1053.  
  1054.         /* Do it to it! */
  1055.         open_progress_monitor(inputFile.name);
  1056.         if (longjmp_reason = setjmp(longjmp_data)) {            /* An error or an abort */
  1057.             switch (longjmp_reason) {
  1058.                 case LONGJMP_ABORT:
  1059.                     WARNMS(&e_methods, "processing aborted.");
  1060.                     e_methods.free_all();    /* clean up memory allocation */
  1061.                     break;
  1062.                 case LONGJMP_ERROR:
  1063.                     break;
  1064.             }
  1065.             fclose(stdin);
  1066.             fclose(stdout);
  1067.             CtoPstr((CSTR)fulloutfile);
  1068.             FSDelete(fulloutfile, dlog_info.outfile.vRefNum);
  1069.             close_progress_monitor();
  1070.             return;
  1071.         }
  1072.  
  1073.         /* print version identification */
  1074.         TRACEMS(&e_methods, 0, FORMAT1("Independent JPEG Group's CJPEG, version %s", JVERSION));
  1075.         TRACEMS(&e_methods, 1, JCOPYRIGHT);
  1076.         TRACEMS(&e_methods, 1, MAC_COPYRIGHT);
  1077.     
  1078.         /* Select the input and output files */
  1079.         PtoCstr(inputFile.name);
  1080.         TRACEMS(&e_methods, 1, FORMAT1("converting %s to jpeg format.", inputFile.name));
  1081.         CtoPstr((CSTR)inputFile.name);
  1082.  
  1083.         TRACEMS(&e_methods, 2, FORMAT1("input file path: %s", fullinfile));
  1084.         freopen((CSTR)fullinfile, "rb", stdin);
  1085.         if ((cinfo.input_file = stdin) == NULL)
  1086.             ERREXIT(&e_methods, FORMAT1("can't open %s", fullinfile));
  1087.  
  1088.         TRACEMS(&e_methods, 2, FORMAT1("output file path: %s", fulloutfile));
  1089.         freopen((CSTR)fulloutfile, "wb", stdout);
  1090.         if ((cinfo.output_file = stdout) == NULL)
  1091.             ERREXIT(&e_methods, FORMAT1("can't open %s", fulloutfile));
  1092.  
  1093.         /* Figure out the input file format, and set up to read it. */
  1094.         select_file_type(&cinfo);
  1095.  
  1096.         set_file_type(dlog_info.outfile, FMT_JPEG);
  1097.         jpeg_compress(&cinfo);
  1098.         progress_monitor(1,1,0,1);
  1099.         Delay(30, &curTicks);
  1100.  
  1101.         close_progress_monitor();
  1102.     }
  1103. }
  1104.  
  1105. /*
  1106.  *    This routine gets called once for each dropped file.  It converts the FSSpec to a full
  1107.  *    path name, checks to see if the file is jpeg or not, and calls the appropriate routine
  1108.  *    to dialog (if necessary) and convert.
  1109.  */
  1110. LOCAL void
  1111. process_a_dropped_file (FSSpec spec)
  1112. {
  1113.     OSErr        rc;
  1114.     Boolean        jpeg=FALSE;
  1115.     int            char1;
  1116.     Str255        fullname;
  1117.  
  1118.     /* convert the FSSpec to a full pathname (for the stdio routines) */
  1119.     if ((rc = FSSpecToName(spec, fullname)) != noErr) {
  1120.         Error(rc, "\pCan't convert file spec to pathname", fullname);
  1121.         return;
  1122.     }
  1123.  
  1124.     /* open file */
  1125.     PtoCstr(fullname);
  1126.     freopen((CSTR)fullname, "rb", stdin);
  1127.     if (stdin == NULL) {
  1128.         CtoPstr((CSTR)fullname);
  1129.         Error(0, "\pError opening input file", fullname);
  1130.         return;
  1131.     }
  1132.     
  1133.     /* check if jpeg */
  1134.     char1 = getchar();
  1135.     if (char1 == -1) {
  1136.         Error(0, "\pUnexpected EOF", "\pCan't read input file");
  1137.         fclose(stdin);
  1138.         return;
  1139.     }
  1140.      if (char1 == 0xff)
  1141.         if (getchar() == 0xd8)
  1142.             jpeg = TRUE;
  1143.  
  1144.     /* close file */
  1145.     fclose(stdin);
  1146.  
  1147.     /* display dialog & convert */
  1148.     if (jpeg)
  1149.         process_a_jpeg_file(spec, (CSTR)fullname);
  1150.     else
  1151.         process_a_nonjpeg_file(spec, (CSTR)fullname);
  1152. }
  1153.  
  1154. LOCAL void
  1155. IntroScreen ()
  1156. {
  1157.     gDropStart = FALSE;        /* the app was started by double-clicking, not dropping */
  1158.  
  1159.     DoAboutBox();
  1160. }
  1161.  
  1162. /*
  1163.  * core AppleEvent handlers - only open documents is interesting
  1164.  *    These event handlers were originally taken from "Flip A BNDL" by Michael S. Engber.
  1165.  *    (modified as needed)
  1166.  */
  1167.  
  1168. METHODDEF pascal OSErr
  1169. AEH_oapp (AppleEvent* ae_p, AppleEvent* reply_p, long refCon)
  1170. {
  1171.     IntroScreen();
  1172.  
  1173.     return noErr;
  1174. }
  1175.  
  1176.  
  1177. METHODDEF pascal OSErr
  1178. AEH_pdoc (AppleEvent* ae_p, AppleEvent* reply_p, long refCon)
  1179. {
  1180.     gDone = TRUE;
  1181.     return errAEEventNotHandled;
  1182. }
  1183.  
  1184.  
  1185. METHODDEF pascal OSErr
  1186. AEH_quit (AppleEvent* ae_p, AppleEvent* reply_p, long refCon)
  1187. {
  1188.     gDone = TRUE;
  1189.     return noErr;
  1190. }
  1191.  
  1192.  
  1193. METHODDEF pascal OSErr
  1194. AEH_odoc (AppleEvent* ae_p, AppleEvent* reply_p, long refCon)
  1195. {
  1196.     long        i;
  1197.     long        docCount;
  1198.     short        purgeCount;
  1199.     Size        sz;
  1200.     OSErr        err;
  1201.     DescType    type;
  1202.     AEDescList    docList;
  1203.     short        wdRefNum;
  1204.     FInfo        finfo;
  1205.  
  1206.     if (gDropStart)
  1207.         gDone = TRUE;
  1208.     
  1209.     /* get the list of docs */
  1210.     if (err = AEGetParamDesc(ae_p, keyDirectObject, typeAEList, &docList)) {
  1211.         Error(err, "\pAEH_odoc", "\pAEGetParamDesc()");
  1212.         return err;
  1213.     }
  1214.     
  1215.     /* make sure there are no other parameters */
  1216.     switch (err = AEGetAttributePtr(ae_p, keyMissedKeywordAttr, typeWildCard, &type, NULL, 0, &sz)) {
  1217.         case errAEDescNotFound:
  1218.             err = noErr;
  1219.             break;
  1220.         case noErr:
  1221.             Error(0,"\pAEH_odoc","\pextra AE attributes");
  1222.             AEDisposeDesc(&docList);
  1223.             return errAEEventNotHandled;
  1224.         default:
  1225.             Error(err,"\pAEH_odoc","\pAEGetAttributePtr()");
  1226.             AEDisposeDesc(&docList);
  1227.             return errAEEventNotHandled;
  1228.     }
  1229.  
  1230.     /* loop through & process the docs */
  1231.     if (err = AECountItems(&docList, &docCount)) {
  1232.         AEDisposeDesc(&docList);
  1233.         return err;
  1234.     }
  1235.  
  1236.     for (i=1; i <= docCount  &&  !gAbort; ++i) {
  1237.         FSSpec        spec;
  1238.         AEKeyword    key;
  1239.  
  1240.         if (err = AEGetNthPtr(&docList, i, typeFSS, &key, &type, (Ptr)&spec, sizeof(spec), &sz)) {
  1241.             Error(err,"\pAEH_odoc","\pAEGetNthPtr()");
  1242.             continue;
  1243.         }
  1244.         
  1245.         if (type != typeFSS) {
  1246.             Error(i, "\pAEH_odoc", "\pNth doclist entry failed coercion to FSSpec");
  1247.             continue;
  1248.         }
  1249.         
  1250.         /* I absolutely won't allow some files to be processed... */
  1251.         err = FSpGetFInfo(&spec, &finfo);
  1252.         if (err == noErr) {
  1253.             /* treat the prefs file like double clicking on the app */
  1254.             if ((finfo.fdType == 'PREF') && (finfo.fdCreator == appSignature)) {
  1255.                 gDropStart = FALSE;
  1256.                 gDone = FALSE;
  1257.                 continue;
  1258.             }
  1259.             /* I just don't want to mess with applications */
  1260.             if (finfo.fdType == 'APPL')
  1261.                 continue;
  1262.         } else {
  1263.             Error(err, "\pCan't get file info.  Ignoring file:", spec.name);
  1264.             continue;
  1265.         }
  1266.         
  1267.         /* Let's be a nice app and set the current directory to where the first file was dropped
  1268.          * from.  I wish all applications did this.  Where else would the user want to save the
  1269.          * file?  With the application?  BAH!  BUT, only do it once so as not to confuse the user
  1270.          * when the directory keeps changing!
  1271.          */
  1272.         if (!gDirChanged) {
  1273.             gDirChanged = TRUE;
  1274.             CurDirStore = spec.parID;
  1275.             SFSaveDisk  = -spec.vRefNum;
  1276.         }
  1277.  
  1278.         process_a_dropped_file(spec);
  1279.     }
  1280.     
  1281.     AEDisposeDesc(&docList);
  1282.     return noErr;
  1283. }
  1284.  
  1285.  
  1286. /*
  1287.  *    Initialize the application.  Init toolbox routines, etc.
  1288.  */
  1289. LOCAL Boolean
  1290. AppInit (void)
  1291. {
  1292.     OSErr        err;
  1293.     int            x;
  1294.     RgnHandle    grayRgn;
  1295.     long        response;
  1296.     long        grow_mem;
  1297.     short        fref_count;
  1298.     short        fref;
  1299.     
  1300.     InitGraf(&thePort);
  1301.     InitFonts();
  1302.     InitWindows();
  1303.     InitMenus();
  1304.     TEInit();
  1305.     InitDialogs(0L);
  1306.  
  1307.     FlushEvents(everyEvent, 0);
  1308.     
  1309.     gTypeListCount = 0;
  1310.     for (err = noErr, fref = 1;  !err;  fref++) {
  1311.         OSType    **frefHand;
  1312.         
  1313.         frefHand = (OSType **)GetIndResource('FREF', fref);
  1314.         err = ResError();
  1315.         
  1316.         if (!err)            
  1317.             if (HomeResFile((Handle)frefHand) != CurResFile())
  1318.                 err = resNotFound;
  1319.  
  1320.         if (!err)
  1321.             if (**frefHand != 'APPL')
  1322.                 if (gTypeListCount == MAX_FILE_TYPES) {
  1323.                     gTypeListCount = -1;                /* Too many, allow everything */
  1324.                     err = resNotFound;
  1325.                 } else
  1326.                     gTypeList[gTypeListCount++] = **frefHand;
  1327.         
  1328.         ReleaseResource((Handle)frefHand);
  1329.     }
  1330.  
  1331.     /* Check for the new traps */
  1332.     gNewDialogMgr = TrapAvailable(_DialogDispatch);
  1333.     gWaitNextEvent = TrapAvailable(_WaitNextEvent);
  1334.  
  1335.     /* Use new GetFile if available */
  1336.     err = Gestalt(gestaltStandardFileAttr, &response);
  1337.     gStandardFile58 = (err == noErr && (response & (1 << gestaltStandardFile58)));
  1338.  
  1339.     /* Check for Apple Events */
  1340.     err = Gestalt(gestaltAppleEventsAttr, &response);
  1341.     gAppleEvents = (err == noErr && (response & (1 << gestaltAppleEventsPresent)));
  1342.     
  1343.     /* Check for color QuickDraw and color monitor */
  1344.     err = Gestalt(gestaltQuickdrawVersion, &response);
  1345.     gColorQuickdraw = (err == noErr && (response >= gestalt8BitQD));
  1346.     if (gColorQuickdraw)
  1347.         gColorQuickdraw = ((**(**GetMainDevice()).gdPMap).pixelSize > 2);
  1348.  
  1349.     /* Check for the support of FSSpecs */
  1350.     err = Gestalt(gestaltFSAttr, &response);
  1351.     gFSSpecSupport = (err == noErr && (response & (1 << gestaltHasFSSpecCalls)));
  1352.  
  1353.     /* open the pref file */
  1354.     read_preference_file();
  1355.  
  1356.     gMenu[APPLE_M] = GetMenu(APPLE_ID);
  1357.     AddResMenu(gMenu[APPLE_M],'DRVR');
  1358.     gMenu[FILE_M] = GetMenu(FILE_ID);
  1359.     gMenu[EDIT_M] = GetMenu(EDIT_ID);
  1360.     for (x=0;  x < MENUCOUNT;  x++)
  1361.         InsertMenu(gMenu[x],0);
  1362.     DrawMenuBar();
  1363.  
  1364.     InitCursor();
  1365.  
  1366.     grayRgn = GetGrayRgn();
  1367.     gDragRect = (**grayRgn).rgnBBox;
  1368.     InsetRect(&gDragRect, 4, 4);
  1369.  
  1370.     gMemory95 = MaxMem(&grow_mem);
  1371.     gMemory95 += grow_mem;
  1372.     gMemory95 -= MEMORY_FUDGE;
  1373.     gMemory95 = (gMemory95 > 0) ? (gMemory95 * 19L) / 20L : 0;    /* free memory - 5% */
  1374.  
  1375.     /* install core AE handlers */
  1376.     if (gAppleEvents) {
  1377.         if ((err = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, &AEH_oapp, 0L, FALSE)) ||
  1378.             (err = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments  , &AEH_odoc, 0L, FALSE)) ||
  1379.             (err = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments , &AEH_pdoc, 0L, FALSE)) ||
  1380.             (err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, &AEH_quit, 0L, FALSE))) {
  1381.                 Error(err, "\pAppInit", "\pAEInstallEventHandler");
  1382.                 return FALSE;
  1383.         }
  1384.     } else {
  1385.         EventRecord theEvent;
  1386.         
  1387.         // Under system 6, you have to do this before displaying the window
  1388.         // or your window will be displayed behind the others until WNE is
  1389.         // finally called.
  1390.         if (gWaitNextEvent)
  1391.             WaitNextEvent(0, &theEvent, 0L, 0L);
  1392.         IntroScreen();
  1393.     }
  1394.     
  1395.     return TRUE;
  1396. }
  1397.  
  1398.  
  1399. /* GetFile: wrapper for StandardGetFile */
  1400. LOCAL void
  1401. GetFile (FileFilterProcPtr fileFilter,
  1402.          short numTypes, 
  1403.          SFTypeList typeList,
  1404.          StandardFileReply *reply)
  1405. {
  1406.     OSErr        err;
  1407.     Point        where;
  1408.     SFReply        oldReply;
  1409.     long        procID;
  1410.     FInfo        fndrInfo;
  1411.  
  1412.  
  1413.     /* Use new GetFile if available */
  1414.     if (gStandardFile58) {
  1415.         StandardGetFile(fileFilter, numTypes, typeList, reply);
  1416.         return;
  1417.     }
  1418.  
  1419.     /* Must use old GetFile */
  1420.     CenterDialog(getDlgID, &where);
  1421.     SFGetFile(where, 0, fileFilter, numTypes, typeList, 0, &oldReply);
  1422.  
  1423.     /* Convert the old reply to a new reply */
  1424.     reply->sfGood = oldReply.good;
  1425.     if (!reply->sfGood)
  1426.         return;
  1427.  
  1428.     reply->sfType = oldReply.fType;
  1429.  
  1430.     /* Convert working directory into volume reference and directory ID */
  1431.     err = GetWDInfo(oldReply.vRefNum, &reply->sfFile.vRefNum, &reply->sfFile.parID, &procID);
  1432.     if (err != noErr) {
  1433.         reply->sfFile.vRefNum = oldReply.vRefNum;
  1434.         reply->sfFile.parID = 0;
  1435.     }
  1436.     COPY(reply->sfFile.name, oldReply.fName);
  1437.  
  1438.     reply->sfScript = smSystemScript;
  1439.  
  1440.     err = HGetFInfo(reply->sfFile.vRefNum, reply->sfFile.parID, reply->sfFile.name, &fndrInfo);
  1441.     reply->sfFlags = (err == noErr) ? fndrInfo.fdFlags : 0;
  1442.  
  1443.     reply->sfIsFolder = FALSE;
  1444.     reply->sfIsVolume = FALSE;
  1445. }
  1446.  
  1447.  
  1448. /* PutFile: Wrapper for StandardPutFile */
  1449. GLOBAL void
  1450. PutFile (ConstStr255Param prompt,
  1451.          ConstStr255Param defaultName,
  1452.          StandardFileReply *reply)
  1453. {
  1454.     OSErr        err;
  1455.     long        response;
  1456.     Point        where;
  1457.     SFReply        oldReply;
  1458.     long        procID;
  1459.     FInfo        fndrInfo;
  1460.  
  1461.     /* Use new PutFile if available */
  1462.     if (gStandardFile58) {
  1463.         StandardPutFile(prompt, defaultName, reply);
  1464.         return;
  1465.     }
  1466.  
  1467.     /* Must use old PutFile */
  1468.     CenterDialog(putDlgID, &where);
  1469.     SFPutFile(where, prompt, defaultName, 0, &oldReply);
  1470.     
  1471.     /* Convert the old reply to a new reply */
  1472.     reply->sfGood = oldReply.good;
  1473.     if (!reply->sfGood)
  1474.         return;
  1475.         
  1476.     /* Convert working directory into volume reference and directory ID */
  1477.     err = GetWDInfo(oldReply.vRefNum, &reply->sfFile.vRefNum, &reply->sfFile.parID, &procID);
  1478.     if (err != noErr) {
  1479.         reply->sfFile.vRefNum = oldReply.vRefNum;
  1480.         reply->sfFile.parID = 0;
  1481.     }
  1482.     
  1483.     COPY(reply->sfFile.name, oldReply.fName);
  1484.     
  1485.     reply->sfScript = smSystemScript;
  1486.     
  1487.     err = HGetFInfo(reply->sfFile.vRefNum, reply->sfFile.parID, reply->sfFile.name, &fndrInfo);
  1488.     reply->sfReplacing = (err != fnfErr);
  1489.     reply->sfIsFolder = FALSE;
  1490.     reply->sfIsVolume = FALSE;
  1491. }
  1492.  
  1493.  
  1494. /* Process a menu selection.  This may be from pulling down the menu, or a command-key. */
  1495. LOCAL void
  1496. DoMenu (menuResult)
  1497.     long menuResult;
  1498. {
  1499.     short                menuID, itemID;
  1500.     Str255                AccessoryName;
  1501.     WindowPtr            savePort;
  1502.     StandardFileReply    sfReply;
  1503.     SFTypeList            typeList;
  1504.  
  1505.     menuID = HiWord(menuResult);
  1506.     itemID = LoWord(menuResult);
  1507.     switch (menuID) {
  1508.         case APPLE_ID:
  1509.             if (itemID == APPLE_ABOUT)
  1510.                 DoAboutBox();
  1511.             else {
  1512.                 GetPort(&savePort);
  1513.                 GetItem(gMenu[APPLE_M], itemID, AccessoryName);
  1514.                 OpenDeskAcc(AccessoryName);
  1515.                 DrawMenuBar();
  1516.                 SetPort(savePort);
  1517.             }
  1518.             break;
  1519.  
  1520.         case FILE_ID:
  1521.             switch (itemID) {
  1522.                 case FILE_OPEN:
  1523.                     gAbort = FALSE;
  1524.                     
  1525.                     if ((**gPrefs).show_all)
  1526.                         GetFile(NULL, -1, typeList, &sfReply);
  1527.                     else
  1528.                         GetFile(NULL, gTypeListCount, gTypeList, &sfReply);
  1529.                         
  1530.                     HiliteMenu(0);
  1531.                     if (sfReply.sfGood) {
  1532.                         gDirChanged = TRUE;
  1533.                         process_a_dropped_file(sfReply.sfFile);
  1534.                     }
  1535.                     break;
  1536.  
  1537.                 case FILE_ABORT:
  1538.                     gAbort = TRUE;
  1539.                     HiliteMenu(0);
  1540.                     longjmp(longjmp_data, LONGJMP_ABORT);
  1541.                     break;
  1542.                 
  1543.                 case FILE_PREFS:
  1544.                     display_prefs_dialog();
  1545.                     break;
  1546.  
  1547.                 case FILE_QUIT:
  1548.                     gDone = TRUE;
  1549.                     gAbort = TRUE;
  1550.                     HiliteMenu(0);
  1551.                     if (gProcessing)
  1552.                         longjmp(longjmp_data, LONGJMP_ABORT);
  1553.                     break;
  1554.             }
  1555.             break;
  1556.  
  1557.         case EDIT_ID:
  1558.             switch (itemID) {
  1559.                 case EDIT_UNDO:
  1560.                 case EDIT_CUT:
  1561.                 case EDIT_COPY:
  1562.                 case EDIT_PASTE:
  1563.                 case EDIT_CLEAR:
  1564.                     if (!SystemEdit(itemID-1)) {
  1565.                     }
  1566.                     break;
  1567.             }
  1568.     }
  1569.     HiliteMenu(0);
  1570. }
  1571.  
  1572.  
  1573. /* The user is about to perform a menu action.  Enable/disable items first. */
  1574. LOCAL void
  1575. UpdateMenuBar ()
  1576. {
  1577.     if (gProcessing)
  1578.         EnableItem(GetMHandle(FILE_ID), FILE_ABORT);
  1579.     else
  1580.         DisableItem(GetMHandle(FILE_ID), FILE_ABORT);
  1581. }
  1582.  
  1583.  
  1584. /* Process an event.  You know, the normal stuff. */
  1585. LOCAL void
  1586. HandleEvent (Event)
  1587.     EventRecord Event;
  1588. {
  1589.     char            c;
  1590.     WindowPtr        mouseWindow;
  1591.     Rect            r;
  1592.     short            ctlVal;
  1593.     short            delta;
  1594.  
  1595.     switch (Event.what) {
  1596.         case kHighLevelEvent:
  1597.             if (!gProcessing)
  1598.                 AEProcessAppleEvent(&Event);
  1599.             else
  1600.                 SysBeep(1);
  1601.             break;
  1602.  
  1603.         case autoKey:
  1604.         case keyDown:
  1605.             c = (char)Event.message;
  1606.             if (Event.modifiers & cmdKey) {
  1607.                 UpdateMenuBar();
  1608.                 DoMenu(MenuKey(c));
  1609.             } else
  1610.                 SysBeep(1);
  1611.             break;
  1612.  
  1613.         case mouseDown:
  1614.             switch (FindWindow(Event.where,&mouseWindow)) {
  1615.                 case inMenuBar:
  1616.                     UpdateMenuBar();
  1617.                     DoMenu(MenuSelect(Event.where));
  1618.                     break;
  1619.                 case inSysWindow:
  1620.                     SystemClick(&Event,mouseWindow);
  1621.                     break;
  1622.                 case inDrag:
  1623.                     DragWindow(mouseWindow, Event.where, &gDragRect);
  1624.                     if (mouseWindow == progressMonitorWindow) {
  1625.                         (**gPrefs).progressLoc = topLeft((**((WindowPeek)mouseWindow)->contRgn).rgnBBox);
  1626.                         ChangedResource((Handle)gPrefs);
  1627.                     }
  1628.                     break;
  1629.                 case inContent:
  1630.                     if (mouseWindow != FrontWindow())
  1631.                         SelectWindow(mouseWindow);
  1632.                     else
  1633.                         if (mouseWindow == progressMonitorWindow) {
  1634.                             DialogPtr        dialog;
  1635.                             short            item;
  1636.                             short            part;
  1637.                             ControlHandle    theControl;
  1638.                             
  1639.                             GlobalToLocal(&Event.where);
  1640.                             
  1641.                             /* WARNING:  This stupid code assumes 1 button and 1 scroll bar */
  1642.                             switch (part = FindControl(Event.where, mouseWindow, &theControl)) {
  1643.                                 case inButton:    /* ABORT */
  1644.                                     if (TrackControl(theControl, Event.where, NULL))
  1645.                                         longjmp(longjmp_data, LONGJMP_ABORT);
  1646.                                     break;
  1647.                                     
  1648.                                 case inUpButton:
  1649.                                 case inDownButton:
  1650.                                 case inPageUp:
  1651.                                 case inPageDown:
  1652.                                     TrackControl(theControl, Event.where, scroll_action_proc);
  1653.                                     break;
  1654.                                     
  1655.                                 case inThumb:
  1656.                                     if (TrackControl(theControl, Event.where, NULL)) {
  1657.                                         ctlVal = GetCtlValue(theControl);
  1658.                                         delta = curErrLine - ctlVal;
  1659.                                         TEScroll(0, delta * (**errText).lineHeight, errText);
  1660.                                         curErrLine = ctlVal;
  1661.                                     }
  1662.                                     break;
  1663.                             }
  1664.                             
  1665.                         }
  1666.                     break;
  1667.             }
  1668.             break;
  1669.  
  1670.         case activateEvt:
  1671.             if (Event.modifiers & activeFlag) {            /* ACTIVATING */
  1672.             } else {                                    /* DEACTIVATING */
  1673.             }
  1674.             break;
  1675.             
  1676.         case osEvt:
  1677.             switch (Event.message >> 24) {
  1678.                 case suspendResumeMessage:
  1679.                     if (Event.message & resumeFlag) {    /* RESUME EVENT */
  1680.                         gInBackground = FALSE;
  1681.                         if (gProcessing)
  1682.                             HiliteControl(CITEMH(progressMonitorWindow,PROG_SCROLL), 0);
  1683.                     } else {                            /* SUSPEND EVENT */
  1684.                         gInBackground = TRUE;
  1685.                         if (gProcessing)
  1686.                             HiliteControl(CITEMH(progressMonitorWindow,PROG_SCROLL), 255);
  1687.                     }
  1688.                     break;
  1689.             }
  1690.             break;
  1691.  
  1692.         case updateEvt:
  1693.             BeginUpdate((WindowPtr)Event.message);
  1694.             if ((WindowPtr)Event.message == progressMonitorWindow)
  1695.                 draw_progress_monitor(TRUE);
  1696.             EndUpdate((WindowPtr)Event.message);
  1697.             break;
  1698.     }
  1699. }
  1700.  
  1701.  
  1702. /* Process all events until a nullEvent is received.  Gets all real events out of the way so that
  1703.  * we can go back to doing what we were doing
  1704.  */
  1705. LOCAL void
  1706. ProcessOutstandingEvents()
  1707. {
  1708.     EventRecord    theEvent;
  1709.     long        waitTime;
  1710.     Boolean        event;
  1711.     
  1712.     if (gInBackground)
  1713.         waitTime = 6;
  1714.     else
  1715.         waitTime = 0;
  1716.  
  1717.     do {
  1718.         if (gWaitNextEvent)
  1719.             event = WaitNextEvent(everyEvent, &theEvent, waitTime, 0L);
  1720.         else {
  1721.             SystemTask();
  1722.             event = GetNextEvent(everyEvent, &theEvent);
  1723.         }
  1724.  
  1725.         if (event)
  1726.             HandleEvent(theEvent);
  1727.     } while (event);
  1728. }
  1729.  
  1730.  
  1731. /*
  1732.  *    Main event loop...
  1733.  */
  1734. void
  1735. main (void)
  1736. {
  1737.     EventRecord    curEvent;
  1738.  
  1739.     if (AppInit())
  1740.         while (!gDone) {
  1741.             if (WaitNextEvent(everyEvent,&curEvent,15L,0L))
  1742.                 HandleEvent(curEvent);
  1743.         }
  1744.     ExitToShell();
  1745. }